From Push/Enter to Eval/Apply by Program Transformation
نویسندگان
چکیده
machine encoding −−−−−→ functional program program transformation −−−−−−−−−−−−→ tail-recursive functional program decoding −−−−−→ abstract machine Our method proposes a new tool for the ‘encoding’ part, as there are multiple choices of how one represents an abstract machine as a functional program. In particular, one can use different data structures to represent the type of the stack of the original machine. In detail, our derivation consists of the following steps: • We start with a PE machine, which is a slight generalisation of a machine proposed by Krivine [10]. It normalises terms to weak-head normal form using the call-by-name evaluation strategy. Then, we give a (big-step [6]) encoding of this machine in Haskell. (We do not use any Haskell-specific features, and the choice of Haskell over any other functional language is in this case arbitrary. The ‘lazy list concatenation’ that we use in this paper is unrelated to the lazy semantics of Haskell.) • We then perform semantics-preserving program transformations on this encoding. They are purely syntactic, that is they are not directed by any semantic understanding of the machine. In detail, we implement the stack using a list with lazy concatenation, and then apply the CPS transformation to reify the recursive calls of the operations that work on the stack as transitions of the machine. • Finally, we decode the resulting EA machine from the program obtained by these transformations. There are some strong similarities between the resulting machines and the application-related rules of the two versions of the STG machine [12] used in the Haskell GHC compiler. Thus our method proves to be useful even when dealing with real-life implementations. 2 The Krivine machine for a language with multi-argument binders We first define λMULT, a version of the lambda calculus in which a λ -abstraction can bind more than one variable at a time, and in which an expression can be applied to a tuple of arguments. Its syntax is given by the following grammar, where e,e0, . . . stand for expressions, x,x1, . . . denote variables, and 〈a1 . . .an〉 is a non-empty tuple containing elements a1, . . . ,an: e ::= x | e0 〈e1 . . .en〉 | λ 〈x1 . . .xn〉.e Intuitively, an expression e0 〈e1 . . .en〉 roughly corresponds to e0e1 · · ·en in the standard lambda calculus, while λ 〈x1 . . .xn〉.e corresponds to λx1. . . .λxn.e. Note that the λMULT calculus is different from the one obtained by simply adding finite products to the standard lambda calculus and treating λ -abstractions as uncurried functions: for example, the term (λ 〈x1 . . .x4〉.e)〈x1 x2〉〈x3 x4〉 has an ‘arity’ mismatch in the latter calculus. We give an operational semantics to λMULT in terms of a PE abstract machine, which normalises a given expression to weak-head normal form using the call-by-name evaluation strategy. The machine is a natural extension of an abstract machine proposed by Krivine [10] (see also Biernacka and Danvy [2]), which was originally defined for a language with multi-argument binders, but with applications limited to a single argument (and not a tuple of arguments). Each machine configuration is a pair (e, s) consisting M. Piróg and J. Gibbons 55 of an expression and a stack of expressions. We write ‘:’ for the stack constructor (the ‘push’ operation), and ε for the empty stack. We write e[e1/x1, . . . ,en/xn] to denote an expression e in which expressions e1, . . . ,en are substituted for variables x1, . . . ,xn respectively. The transition rules are as follows: (e0 〈e1 . . .en〉, s)⇒ (e0, e1: · · · :en:s) (K-APP) (λ 〈x1 . . .xn〉.e, e1: · · · :en:s)⇒ (e[e1/x1, . . . ,en/xn], s) (K-FUN) The initial configuration of the machine for an expression e is (e, ε). The transition K-APP evaluates applications: it pushes the arguments on the stack and continues with the head of the application. The transition K-FUN deals with abstractions: if the abstraction needs n arguments and there are at least n expressions on the stack, the machine continues with the body of the abstraction, with the appropriate variables substituted. The machine halts when there are no transitions that match the left-hand sides of K-APP or K-FUN, that is, when trying to evaluate an application for which there are not enough arguments on the stack or when trying to evaluate a free variable. 3 Haskell implementation Now, we present a deep embedding of λMULT and the machine in Haskell. The definition of terms is as follows: type Identifier = ... data Term = Var Identifier | App Term [Term] | Fun [Identifier] Term subst :: [(Term, Identifier)] -> Term -> Term subst = ... Neither the choice of the type of identifiers nor any concrete implementation of subst affect the derivation in any way. The Haskell encoding of the machine uses an abstract datatype representing the stack of the machine. It has two operations: type Stack a = ... push :: [a] -> Stack a -> Stack a pop :: Int -> Stack a -> Maybe ([a], Stack a) In the above, the function push places a tuple of values on top of the stack. The function pop n s attempts to extract the first n values from the top of the stack s. If the stack contains too few elements, pop returns Nothing. The most obvious implementation of this interface uses the list datatype:
منابع مشابه
Making a fast curry: push/enter vs. eval/apply for higher-order languages SIMON MARLOW and SIMON PEYTON JONES
Higher-order languages that encourage currying are typically implemented using one of two basic evaluation models: push/enter or eval/apply. Implementors use their intuition and qualitative judgements to choose one model or the other. Our goal in this paper is to provide, for the first time, a more substantial basis for this choice, based on our qualitative and quantitative experience of implem...
متن کاملA functional correspondence between call-by-need evaluators and lazy abstract machines
We bridge the gap between compositional evaluators and abstract machines for the lambda-calculus, using closure conversion, transformation into continuation-passing style, and defunctionalization of continuations. This article is a spin-off of our article at PPDP 2003, where we consider call by name and call by value. Here, however, we consider call by need. We derive a lazy abstract machine fr...
متن کاملA Type System for the Push-Enter Model
ved. Compiling with typed intermediate languages has many advantages including that the properties of traditional compilation methods can be specified by types and they can be verified automatically in compile-time [5]. The push-enter model is an important class of compilation methods for higher-order functions, as has been demonstrated by the ZINC machine (which is used in the OCaml compiler) ...
متن کاملFrom natural semantics to C: A formal derivation of two STG machines
The Spineless Tag-less G-machine (STG machine) was defined as the target abstract machine for compiling the lazy functional language Haskell. It is at the heart of the Glasgow Haskell Compiler (GHC) which is claimed to be the Haskell compiler that generates the most efficient code. A high-level description of the STG machine can be found at (Peyton Jones, 1992; Marlow & Peyton Jones, 2004; Marl...
متن کاملArrival first queueing networks with applications in kanban production systems
In this paper we introduce a new class of queueing networks called arrival first networks. We characterise its transition rates and derive the relationship between arrival rules, linear partial balance equations, and product form stationary distributions. This model is motivated by production systems operating under a kanban protocol. In contrast with the conventional departure first networks, ...
متن کامل